﻿using AiBLSmartEdu.Module.SmartStockSense.API.DTOs;
using AutoMapper;
using Domain.Entities.Stocks;
using FrameworkCore.DI;
using FrameworkCore.Extensions;
using FrameworkCore.Helpers;
using FrameworkCore.Repositories;
using Microsoft.EntityFrameworkCore;
using System.Linq.Expressions;
using System.Text.Json;

namespace AiBLSmartEdu.Module.SmartStockSense.API.Services;

public class StockServices : IScopedDependency
{
    private readonly IRepository<Stock> _stockRepository;
    private readonly IMapper _mapper;
    private readonly StockDataProvider _stockDataProvider;
    private readonly IRepository<StockInformation> _stockInformationRepository;
    private readonly KDJPredictionProvider _kDJPredictionProvider;

    public StockServices(
        IRepository<Stock> stockRepository,
        IMapper mapper,
        StockDataProvider stockDataProvider,
        IRepository<StockInformation> stockInformationRepository,
        KDJPredictionProvider kDJPredictionProvider)
    {
        _stockRepository = stockRepository;
        _mapper = mapper;
        _stockDataProvider = stockDataProvider;
        _stockInformationRepository = stockInformationRepository;
        _kDJPredictionProvider = kDJPredictionProvider;
    }

    /// <summary>
    /// 查询
    /// </summary>
    /// <param name="keyword">关键字</param>
    /// <returns></returns>
    public async Task<IEnumerable<StockOutputDto>> SearchAsync(string keyword) 
    {
        if (string.IsNullOrWhiteSpace(keyword))
        {
            return Enumerable.Empty<StockOutputDto>(); 
        }

        var stocks = await _stockRepository
            .Query(x => x.StockCode.StartsWith(keyword) || x.StockCode.EndsWith(keyword) || x.StockName.Contains(keyword))
            .ToListAsync();

        return _mapper.Map<IEnumerable<StockOutputDto>>(stocks); // 使用 IEnumerable 而不是 List
    }

    /// <summary>
    /// 预测趋势
    /// </summary>
    /// <param name="stockCode">股票代码</param>
    /// <returns></returns>
    public async Task<PredictTrendDto> PredictTrendAsync(string stockCode) 
    {
        var stock = await _stockRepository.Query(x => x.StockCode == stockCode).FirstOrDefaultAsync();
        if (stock == null) return new PredictTrendDto();

        var stockInformation = await _stockInformationRepository
            .Query(x => x.Stock.StockCode == stockCode && x.Type == StockDataType.KDJ)
            .OrderByDescending(x => x.Timestamp) // 按照时间降序排列
            .FirstOrDefaultAsync(); // 获取最新的一个记录
        if (stockInformation == null|| string.IsNullOrWhiteSpace(stockInformation.JSON)) 
            return  new PredictTrendDto();

        var kdjDataList = JsonSerializer.Deserialize<List<KDJTechnicalIndicatorDto>>(stockInformation.JSON) 
            ?? new List<KDJTechnicalIndicatorDto>();

        return  _kDJPredictionProvider.PredictTrend(kdjDataList);
    }

    /// <summary>
    /// 获取今天用户查询的股票列表
    /// </summary>
    /// <returns></returns>
    public async Task<IEnumerable<StockInformation>> GetTodayUserSearchStockListAsync()
    {
        var todayUserSearchStockList = await _stockInformationRepository
            .Query(x => x.Timestamp.Day == DateTime.Now.Day)
            .Include(x => x.Stock)
            .ToListAsync();

        return todayUserSearchStockList;
    }

    /// <summary>
    /// 创建股票基本信息
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task CreateAsync(StockCreateInputModel input)
    {
        var model = _mapper.Map<Stock>(input);
        await _stockRepository.InsertAsync(model);
    }

    /// <summary>
    /// 创建股票基本信息
    /// </summary>
    /// <param name="inputs"></param>
    /// <param name="isCheckDuplicates">是否检查重复项</param>
    /// <returns></returns>
    public async Task CreateAsync(IEnumerable<StockCreateInputModel> inputs, bool isCheckDuplicates = true)
    {
        if (inputs == null || !inputs.Any()) return;

        var models = _mapper.Map<IEnumerable<Stock>>(inputs).ToList();
        var uniqueModels = new List<Stock>();

        if (isCheckDuplicates)
        {
            var existingStocks = await _stockRepository.Query(x => models.Select(s => s.StockCode).Contains(x.StockCode)).ToListAsync();
            var existingStockCodes = existingStocks.Select(s => s.StockCode).ToHashSet();

            uniqueModels = models.Where(model => !existingStockCodes.Contains(model.StockCode)).ToList();
        }
        else
        {
            uniqueModels = models;
        }

        if (uniqueModels.Any())
        {
            await _stockRepository.InsertAsync(uniqueModels);
        }
    }

    /// <summary>
    /// 创建股票详情信息
    /// </summary>
    /// <param name="stockCode"></param>
    /// <param name="type"></param>
    /// <returns></returns>
    public async Task CreateStockInformationAsync(string stockCode, StockDataType type)
    {
        var stock = await _stockRepository.Query(x => x.StockCode == stockCode).FirstOrDefaultAsync();
        if (stock == null) return;

        bool existingStockInformation = await _stockInformationRepository.Query(x =>
            x.Timestamp.Date == DateTime.Now.Date &&
            x.Stock.StockCode == stockCode &&
            x.Type == type).AnyAsync();

        if (existingStockInformation) return;

        var stockInformation = new StockInformation
        {
            StockId = stock.Id,
            Timestamp = DateTime.Now.Date,
            Type = type,
            JSON = await GetStockDataAsync(stockCode, type)
        };

        if (!string.IsNullOrEmpty(stockInformation.JSON))
        {
            await _stockInformationRepository.InsertAsync(stockInformation);
        }
    }

    /// <summary>
    /// 更新股票详情信息
    /// </summary>
    /// <param name="stockCode"></param>
    /// <param name="type"></param>
    /// <returns></returns>
    public async Task UpdateStockInformationAsync(string stockCode, StockDataType type)
    {
        var stock = await _stockRepository.Query(x => x.StockCode == stockCode).FirstOrDefaultAsync();
        if (stock == null) return;

        var stockInformation = await _stockInformationRepository.Query(x =>
            x.Timestamp.Date == DateTime.Now.Date &&
            x.Stock.StockCode == stockCode &&
            x.Type == type).FirstOrDefaultAsync();

        if (stockInformation == null)
        {
            await CreateStockInformationAsync(stockCode, type);
             stockInformation = await _stockInformationRepository.Query(x =>
             x.Timestamp.Date == DateTime.Now.Date &&
             x.Stock.StockCode == stockCode &&
             x.Type == type).FirstOrDefaultAsync()!;
        }
        if (stockInformation == null) return ;

        stockInformation.JSON = await GetStockDataAsync(stockCode, type);

        if (!string.IsNullOrEmpty(stockInformation.JSON))
        {
            await _stockInformationRepository.UpdateAsync(x => x.SetProperty(x => x.JSON, stockInformation.JSON), x => x.Id == stockInformation.Id);
        }
    }

    /// <summary>
    /// 股票是否存在 
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public async Task<bool> ExistingAsync(long? id = null)
    {
        if (id.HasValue)
        {
            return await _stockRepository.GetByIdAsync(id) != null;
        }

        return await _stockRepository.Query(x => x.Id > 0).AnyAsync();
    }

    /// <summary>
    /// 获取股票信息
    /// </summary>
    /// <returns></returns>
    public async Task<StockInformationDto> GetStockInformationAsync(StockInformationFilter filter)
    {
        var toDayDate = DateTime.Now.Date;
        var stockDataType = filter.Type ?? StockDataType.Trading;

        // 构建基本过滤表达式
        Expression<Func<StockInformation, bool>> filterExpression = x =>
            x.Stock.StockCode == filter.StockCode && x.Type == stockDataType;

        // 日期验证和添加过滤条件
        if (filter.StartTime.HasValue && filter.EndTime.HasValue)
        {
            ValidateDateRange(filter.StartTime.Value, filter.EndTime.Value, toDayDate);

            filterExpression = filterExpression.AndAlso(x => x.Timestamp.Date == filter.EndTime.Value.Date);
        }
        else
        {
            filterExpression = filterExpression.AndAlso(x => x.Timestamp.Date == toDayDate);
        }

        // 创建或更新股票信息
        await CreateStockInformationAsync(filter.StockCode, stockDataType);

        // 查询股票信息
        var stockInformation = await _stockInformationRepository
            .Query(filterExpression)
            .Include(i => i.Stock)
            .FirstOrDefaultAsync();
        if (stockInformation == null) return new StockInformationDto();

        if (!string.IsNullOrWhiteSpace(stockInformation.JSON))
        {
            // 更新 JSON 数据
            stockInformation.JSON = FilterData(stockInformation.JSON, filter, toDayDate);
        }

        var stock =  _mapper.Map<StockInformationDto>(stockInformation);
        stock.StockName = stockInformation.Stock.StockName;

        return stock;
    }

    /// <summary>
    /// 获取股票详细信息
    /// </summary>
    /// <param name="stockCode"></param>
    /// <param name="type"></param>
    /// <returns></returns>
    private async Task<string> GetStockDataAsync(string stockCode, StockDataType type)
    {
        switch (type)
        {
            case StockDataType.Trading:
                var tradingData = await _stockDataProvider.GetHistoricalMinuteTradingsAsync(stockCode);
                return tradingData?.Count() > 0 ? JsonSerializer.Serialize(tradingData) : string.Empty;
            case StockDataType.BOLL:
                var bollData = await _stockDataProvider.GetBollTechnicalIndicatorsAsync(stockCode);
                return bollData?.Count() > 0 ? JsonSerializer.Serialize(bollData) : string.Empty;
            case StockDataType.MA:
                var maData = await _stockDataProvider.GetMATechnicalIndicatorsAsync(stockCode);
                return maData?.Count() > 0 ? JsonSerializer.Serialize(maData) : string.Empty;
            case StockDataType.MACD:
                var macdData = await _stockDataProvider.GetMACDTechnicalIndicatorsAsync(stockCode);
                return macdData?.Count() > 0 ? JsonSerializer.Serialize(macdData) : string.Empty;
            case StockDataType.KDJ:
                var kdjData = await _stockDataProvider.GetKDJTechnicalIndicatorsAsync(stockCode);
                return kdjData?.Count() > 0 ? JsonSerializer.Serialize(kdjData) : string.Empty;
            case StockDataType.FiveLevelMarket:
                var fiveLevelMarketData = await _stockDataProvider.GetFiveLevelMarketTechnicalIndicatorsAsync(stockCode);
                return fiveLevelMarketData?.Count() > 0 ? JsonSerializer.Serialize(fiveLevelMarketData) : string.Empty;
            default:
                return string.Empty;
        }
    }

    // 验证日期范围
    private void ValidateDateRange(DateTime startTime, DateTime endTime, DateTime toDayDate)
    {
        var dateDifference = (endTime - startTime).TotalDays;
        ExceptionHelper.ThrowIfTrue(dateDifference > 31, "只能查找一个月以内的");
        ExceptionHelper.ThrowIfTrue(startTime > toDayDate || endTime > toDayDate, "日期不能大于今天的日期");
    }

    private string FilterData(string jsonData, StockInformationFilter filter, DateTime toDayDate) 
    {
        switch (filter.Type)
        {
            case StockDataType.Trading:
                var tradingData = FilterHistoricalMinuteTradingData(jsonData, filter, toDayDate);
                return tradingData?.Count > 0 ? JsonSerializer.Serialize(tradingData) : string.Empty;
            case StockDataType.KDJ:
                var kdjData = FilterKDJTechnicalData(jsonData, filter, toDayDate);
                return kdjData?.Count > 0 ? JsonSerializer.Serialize(kdjData) : string.Empty;
            case StockDataType.MACD:
                var macdData = FilterMACDTechnicalData(jsonData, filter, toDayDate);
                return macdData?.Count > 0 ? JsonSerializer.Serialize(macdData) : string.Empty;
            case StockDataType.MA:
                var maData = FilterMATechnicalData(jsonData, filter, toDayDate);
                return maData?.Count > 0 ? JsonSerializer.Serialize(maData) : string.Empty;
            case StockDataType.BOLL:
                var bollData = FilterBollTechnicalData(jsonData, filter, toDayDate);
                return bollData?.Count > 0 ? JsonSerializer.Serialize(bollData) : string.Empty;
            case StockDataType.FiveLevelMarket:
                var fiveLevelMarketData = FilterFiveLevelMarketTechnicalData(jsonData, filter, toDayDate);
                return fiveLevelMarketData?.Count > 0 ? JsonSerializer.Serialize(fiveLevelMarketData) : string.Empty;
            default:
                return string.Empty;
        }
    }

    private List<HistoricalMinuteTradingDto> FilterHistoricalMinuteTradingData(string jsonData, StockInformationFilter filter, DateTime toDayDate)
    {
        var data = JsonSerializer.Deserialize<List<HistoricalMinuteTradingDto>>(jsonData) ?? new List<HistoricalMinuteTradingDto>();

        if (filter.IsRecentDay.HasValue && filter.IsRecentDay.Value)
        {
            var recentDate = data.Max(x => x.D?.Date);
            return data.Where(x => x.D?.Date == recentDate).ToList();
        }

        var filterStartDate = filter.StartTime?.Date ?? toDayDate;
        var filterEndDate = filter.EndTime?.Date ?? toDayDate;

        return data.Where(x => x.D?.Date >= filterStartDate && x.D?.Date <= filterEndDate).ToList();
    }

    private List<KDJTechnicalIndicatorDto> FilterKDJTechnicalData(string jsonData, StockInformationFilter filter, DateTime toDayDate)
    {
        var data = JsonSerializer.Deserialize<List<KDJTechnicalIndicatorDto>>(jsonData) ?? new List<KDJTechnicalIndicatorDto>();

        if (filter.IsRecentDay.HasValue && filter.IsRecentDay.Value)
        {
            var recentDate = data.Max(x => x.T?.Date);
            return data.Where(x => x.T?.Date == recentDate).ToList();
        }

        var filterStartDate = filter.StartTime?.Date ?? toDayDate;
        var filterEndDate = filter.EndTime?.Date ?? toDayDate;

        return data.Where(x => x.T?.Date >= filterStartDate && x.T?.Date <= filterEndDate).ToList();
    }

    private List<MACDTechnicalIndicatorDto> FilterMACDTechnicalData(string jsonData, StockInformationFilter filter, DateTime toDayDate)
    {
        var data = JsonSerializer.Deserialize<List<MACDTechnicalIndicatorDto>>(jsonData) ?? new List<MACDTechnicalIndicatorDto>();
        if (filter.IsRecentDay.HasValue && filter.IsRecentDay.Value)
        {
            var recentDate = data.Max(x => x.T?.Date);
            return data.Where(x => x.T?.Date == recentDate).ToList();
        }

        var filterStartDate = filter.StartTime?.Date ?? toDayDate;
        var filterEndDate = filter.EndTime?.Date ?? toDayDate;

        return data.Where(x => x.T?.Date >= filterStartDate && x.T?.Date <= filterEndDate).ToList();
    }

    private List<MATechnicalIndicatorDto> FilterMATechnicalData(string jsonData, StockInformationFilter filter, DateTime toDayDate)
    {
        var data = JsonSerializer.Deserialize<List<MATechnicalIndicatorDto>>(jsonData) ?? new List<MATechnicalIndicatorDto>();
        if (filter.IsRecentDay.HasValue && filter.IsRecentDay.Value)
        {
            var recentDate = data.Max(x => x.T?.Date);
            return data.Where(x => x.T?.Date == recentDate).ToList();
        }

        var filterStartDate = filter.StartTime?.Date ?? toDayDate;
        var filterEndDate = filter.EndTime?.Date ?? toDayDate;

        return data.Where(x => x.T?.Date >= filterStartDate && x.T?.Date <= filterEndDate).ToList();
    }

    private List<BollTechnicalIndicatorDto> FilterBollTechnicalData(string jsonData, StockInformationFilter filter, DateTime toDayDate)
    {
        var data = JsonSerializer.Deserialize<List<BollTechnicalIndicatorDto>>(jsonData) ?? new List<BollTechnicalIndicatorDto>();

        if (filter.IsRecentDay.HasValue && filter.IsRecentDay.Value)
        {
            var recentDate = data.Max(x => x.T?.Date);
            return data.Where(x => x.T?.Date == recentDate).ToList();
        }

        var filterStartDate = filter.StartTime?.Date ?? toDayDate;
        var filterEndDate = filter.EndTime?.Date ?? toDayDate;

        return data.Where(x => x.T?.Date >= filterStartDate && x.T?.Date <= filterEndDate).ToList();
    }

    private List<FiveLevelMarketTechnicalIndicatorDto> FilterFiveLevelMarketTechnicalData(string jsonData, StockInformationFilter filter, DateTime toDayDate)
    {
        var data = JsonSerializer.Deserialize<List<FiveLevelMarketTechnicalIndicatorDto>>(jsonData) ?? new List<FiveLevelMarketTechnicalIndicatorDto>();

        if (filter.IsRecentDay.HasValue && filter.IsRecentDay.Value)
        {
            var recentDate = data.Max(x => x.T?.Date);
            return data.Where(x => x.T?.Date == recentDate).ToList();
        }

        var filterStartDate = filter.StartTime?.Date ?? toDayDate;
        var filterEndDate = filter.EndTime?.Date ?? toDayDate;

        return data.Where(x => x.T?.Date >= filterStartDate && x.T?.Date <= filterEndDate).ToList();
    }
}
